﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Jayden.Dll.Core.Logging
{
	public class Logger
	{
		private static Logger m_Global;
		public static Logger Global
		{
			get
			{
				if (m_Global == null)
				{
					lock (typeof(Logger))
					{
						if (m_Global != null)
						{
							m_Global = GetLogger("global");
						}
					}
				}
				return m_Global;
			}
		}

		private Level m_Level = Level.All;
		private string m_Name;
		private Logger m_Parent;
		private bool m_UseParentHandlers = true;
		private Filter m_Filter = null;
		private List<Handler> m_Handlers = new List<Handler>();

		protected Logger(string name) { m_Name = name; }

		public string Name { get { return m_Name; } }
		public Level Level { get { return m_Level; } set { m_Level = value; } }
		public Logger Parent { get { return m_Parent; } set { m_Parent = value; } }
		public bool UseParentHandlers { get { return m_UseParentHandlers; } set { m_UseParentHandlers = value; } }
		public Filter Filter { get { return m_Filter; } set { m_Filter = value; } }
		public ReadOnlyCollection<Handler> Handlers { get { return m_Handlers.AsReadOnly(); ; } }

		public void RemoveHandler(Handler handler) { m_Handlers.Remove(handler); }
		public void AddHandler(Handler handler) { m_Handlers.Add(handler); }

		public bool IsLoggable(Level level)
		{
			if (m_Level == null)
				return true;
			if (level == null)
				return false;
			return m_Level.Value <= level.Value;
		}

		public void Log(LogRecord record)
		{
			if (IsLoggable(record.Level) && (Filter == null || Filter.IsLoggable(record)))
			{
				foreach (Handler handler in m_Handlers)
					if (handler.IsLoggable(record))
						handler.Publish(record);
				if (UseParentHandlers && Parent != null)
					Parent.Log(record);
			}
		}
		public void Log(Level level, string message) { Log(new LogRecord(m_Name, level, message)); }
		public void Log(Level level, string message, object param) { Log(level, message, new object[] { param }); }
		public void Log(Level level, string message, object[] param)
		{
			LogRecord record = new LogRecord(m_Name, level, message);
			record.Parameters = param;
			Log(record);
		}
		public void Log(Level level, string message, Exception exc)
		{
			LogRecord record = new LogRecord(m_Name, level, message);
			record.Exception = exc;
			Log(record);
		}

		public void LogP(Level level, string sourceClass, string sourceMethod, string message)
		{
			LogRecord record = new LogRecord(m_Name, level, message);
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			Log(record);
		}
		public void LogP(Level level, string sourceClass, string sourceMethod, string message, object param) { LogP(level, sourceClass, sourceMethod, message, new object[] { param }); }
		public void LogP(Level level, string sourceClass, string sourceMethod, string message, object[] param)
		{
			LogRecord record = new LogRecord(m_Name, level, message);
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			record.Parameters = param;
			Log(record);
		}
		public void LogP(Level level, string sourceClass, string sourceMethod, string message, Exception exc)
		{
			LogRecord record = new LogRecord(m_Name, level, message);
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			record.Exception = exc;
			Log(record);
		}

		public void Config(string message) { Log(Level.Config, message); }
		public void Config(string message, params object[] parameters) { Config(string.Format(message, parameters)); }
		public void Fine(string message) { Log(Level.Fine, message); }
		public void Fine(string message, params object[] parameters) { Fine(string.Format(message, parameters)); }
		public void Finer(string message) { Log(Level.Finer, message); }
		public void Finer(string message, params object[] parameters) { Finer(string.Format(message, parameters)); }
		public void Finest(string message) { Log(Level.Finest, message); }
		public void Finest(string message, params object[] parameters) { Finest(string.Format(message, parameters)); }
		public void Info(string message) { Log(Level.Info, message); }
		public void Info(string message, params object[] parameters) { Info(string.Format(message, parameters)); }
		public void Severe(string message) { Log(Level.Severe, message); }
		public void Severe(string message, params object[] parameters) { Severe(string.Format(message, parameters)); }
		public void Warning(string message) { Log(Level.Warning, message); }
		public void Warning(string message, params object[] parameters) { Warning(string.Format(message, parameters)); }

		public void Entering(string sourceClass, string sourceMethod)
		{
			LogRecord record = new LogRecord(m_Name, Level.Finer, "ENTRY");
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			Log(record);
		}
		public void Entering(string sourceClass, string sourceMethod, object param) { Entering(sourceClass, sourceMethod, new object[] { param }); }
		public void Entering(string sourceClass, string sourceMethod, object[] param)
		{
			LogRecord record = new LogRecord(m_Name, Level.Finer, string.Format("ENTRY {0}", string.Join(", ", param)));
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			record.Parameters = param;
			Log(record);
		}

		public void Exiting(string sourceClass, string sourceMethod)
		{
			LogRecord record = new LogRecord(m_Name, Level.Finer, "RETURN");
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			Log(record);
		}
		public void Exiting(string sourceClass, string sourceMethod, object result)
		{
			LogRecord record = new LogRecord(m_Name, Level.Finer, string.Format("RETURN {0}", result));
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			Log(record);
		}

		public void Throwing(string sourceClass, string sourceMethod, Exception exc) { Throwing(Level.Finer, sourceClass, sourceMethod, exc); }
		public void Throwing(Level level, string sourceClass, string sourceMethod, Exception exc)
		{
			Level lvl = null;
			if (exc is Jayden.Dll.Core.Exceptions.Exception)
				lvl = (exc as Jayden.Dll.Core.Exceptions.Exception).ExceptionLevel;
			if (lvl == null)
				lvl = level;
			LogRecord record = new LogRecord(m_Name, lvl, string.Format("THROW"));
			record.SourceClassName = sourceClass;
			record.SourceMethodName = sourceMethod;
			record.Exception = exc;
			Log(record);
		}

		public static Logger GetLogger(string name)
		{
			Logger logger = LogManager.Instance.GetLogger(name);
			if (logger == null)
			{
				lock (LogManager.Instance)
				{
					logger = LogManager.Instance.GetLogger(name);
					if (logger == null)
					{
						logger = new Logger(name);
						logger.UseParentHandlers = true;
						int indexDot = name.IndexOf('.');
						if (indexDot >= 0)
						{
							string parentName = name.Substring(0, indexDot);
							logger.Parent = GetLogger(parentName);
						}
						else if(!string.IsNullOrEmpty(name))
							logger.Parent = GetLogger(string.Empty);
						LogManager.Instance.AddLogger(logger);
					}
				}
			}
			return logger;
		}
		public static Logger GetAnonymousLogger()
		{
			Logger logger = new Logger("anonymous");
			logger.Parent = GetLogger(string.Empty);
			return logger;
		}
	}
}
